#include "head.h"

/* Usage: 接收客户端上传的文件 */
//
void send_flag(int net_fd, int do_flag){
    //printf("错误\n");
    int res_send = send(net_fd, &do_flag, sizeof(int), MSG_NOSIGNAL);
    ERROR_CHECK(res_send, -1, "send");
}

//快传或断点
int handleFastOrResume(MYSQL *conn, int net_fd, char *client_path, char *fileName, char *md5, MYSQL_RES *result, int father_id, int user_id){
    sql_t my_sql;
    my_sql.result = result;
    my_sql.row =  mysql_fetch_row(my_sql.result);
    while (result->row) {
        // SQL语句结果的列数mysql_num_fields(result)
        for(unsigned int i=0; i < mysql_num_fields(result); i++){
            //printf("%s \t", row[i]);
            if(atoi(result->row[3]) == 1){
                //秒传
                //
                //标记位为0的文件不会显示
                //printf("之前自己没传完，后面有人传了完整的，属于快传，修改标记为\n");
                printf("秒传\n");
                //插入一条数据到file表
                //这里有一个父ID要查询
                mysql_free_result(my_sql.result);
                memset(my_sql.query, 0, sizeof(my_sql.query));
                sprintf(my_sql.query, "insert into file_table value(NULL, 0, %d, 1, '%s', '%s', '%s', %d)", 
                        father_id, client_path, fileName, md5, user_id);
                printf("insert1->query = %s\n", my_sql.query);
                int res = mysql_query(conn, my_sql.query);
                if(!res){
                    return 2;
                }else{
                    return 6;
                }
            }
        }   
        //printf(" \n");
    }
    memset(my_sql.query, 0, sizeof(my_sql.query));
    //遍历完没有文件标记位为1，说明是断点重传
    sprintf(my_sql.query, "insert into file_table value(NULL, 0, %d, 0, '%s', '%s', '%s', %d)", father_id, client_path, fileName, md5, user_id); 

    printf("insert1->query = %s\n", my_sql.query);
    int res = mysql_query(conn, my_sql.query);
    if(!res){
        return 4;
    }else{
        return 6;
    }
}

//处理自己没有记录的情况
int handleNoFileRecord(MYSQL *conn, int net_fd, char *client_path, char *fileName, char *md5, int father_id, int user_id){
    sql_t my_sql;
    //int do_flag = 0;
    sprintf(my_sql.query, "select * from file_table where file_has = '%s'", md5);

    int res = mysql_query(conn, my_sql.query);                                                                

    printf("res = %d\n", res);
    MYSQL_ERROR_CHECK(conn,res, 1);

    my_sql.result = mysql_store_result(conn);

    printf("列数：%d\n", mysql_num_fields(my_sql.result));

    my_sql.row = mysql_fetch_row(my_sql.result);

    int num2 = mysql_num_rows(my_sql.result);
    mysql_free_result(my_sql.result);
    printf("num2 in NoFileRe = %d\n", num2);
    if(num2 == 0){
        //上传1
        memset(&(my_sql.query), 0, sizeof(my_sql.query));
        sprintf(my_sql.query, "insert into file_table value(NULL, 0, %d, 0, '%s', '%s', '%s', %d)", father_id, client_path, fileName, md5, user_id);

        int res = mysql_query(conn, my_sql.query);                                                                

        printf("res = %d\n", res);

        MYSQL_ERROR_CHECK(conn,res, 1);


        return 1;
    }else{
        //其他人有的情况，断点或快传
        int res_insert1 =handleFastOrResume(conn, net_fd, client_path, fileName, md5, my_sql.result, father_id, user_id);
        if(res_insert1 == 2){
            return 2;
        }else if(res_insert1 == 4){
            return 4;
        }else if(res_insert1 == 6){
            return 6;
        }
    }
    return 0;
}



//处理自己有的情况
int handleFileExistRecord(MYSQL *conn, int net_fd, char *client_path, char *fileName, char *md5, int file_flag, char *file_md5, int user_id){

    //sql_t *update1 = (sql_t *)calloc(1, sizeof(sql_t));
    //sprintf(update1->query,"update file_table set flag=1 where path = '%s' and user_id = 1", client_path);
    //hash一样
    if(strcmp(md5, file_md5) == 0){
        if(file_flag == 1){
            printf("已有文件\n");
            return 3;
        }
        else{
            //断点续传
            //先查其他人又没有完整的
            sql_t my_sql;
            memset(my_sql.query, 0, sizeof(my_sql.query));
            sprintf(my_sql.query, "select * from file_table where file_has = '%s'", md5);
            //printf("1.1\n");
            int res3 = mysql_query(conn, my_sql.query);
            //printf("1.2\n");
            if(!res3){
                my_sql.result = mysql_store_result(conn);
                my_ulonglong num3 = mysql_num_rows(my_sql.result);
                printf("num3 = %lld\n", num3);
                if(num3 == 0){//没有查到
                    return 7;
                }else{//查到，找file-flag为1的
                    my_sql.row = mysql_fetch_row(my_sql.result);
                    while(my_sql.row != NULL){
                        if(atoi(my_sql.row[3]) == 1){//其他人有一个传过完整的,秒传
                            memset(my_sql.query, 0, sizeof(my_sql.row));
                            sprintf(my_sql.query, "update file_table set flag = 1 where path = '%s' and user_id = %d", client_path, user_id);
                            printf("insert1->query = %s\n", my_sql.query);
                            int res_update1 = mysql_query(conn, my_sql.query);
                            if(!res_update1){
                                return 2;
                            }else{
                                return 6;
                            }
                        }
                        my_sql.row = mysql_fetch_row(my_sql.result);
                    }
                    mysql_free_result(my_sql.result);
                    //到这里没有找到file-flag是1的，断点续传
                    return 7;
                }
            }else{//错误
                return 6;
            }

        }
    }else{//hash不一样
        //同名文件
        printf("同名文件");
        return 5;
    }    
    return 0;
}




/*加一个conn*/
int recvFile(int net_fd,char *path, MYSQL *conn, user_info info){

    //char *path = info.path;


    int flag_server_recv = 0; //文件标志位，0表示客户端没传文件，1表示有文件可传

    //接收文件标记位，判断是否有文件接收
    int recv1 = recv(net_fd, &flag_server_recv, sizeof(flag_server_recv), MSG_WAITALL);
    ERROR_CHECK(recv1, -1, "recv");
    if(flag_server_recv == 0){
        //printf("没有该文件\n");
        close(net_fd);
        return 0;
    }

    int user_id = atoi(info.username);
    char *fileName = info.command_str_goto;
    char *client_f_path = info.path;
    //客户端路径
    //char path[1024] = "/home/wl/linux/project/network_disk/server/wl";
    //拼接路径,将客户端上传的文件放在客户端对应的目录下
    //strcat(path,"/");
    //strcat(path,fileName);

    // 先接收文件整体大小
    off_t file_size = 0;
    //MSG_WAITALL,解决半包问题，接收到指定字节的数据才返回
    recv(net_fd, &file_size, sizeof(off_t), MSG_WAITALL);

    ////文件名长度
    //int name_len = 0;
    //recv(net_fd, &name_len, sizeof(int), MSG_WAITALL);

    ////文件名
    //char fileName[60] = {0};
    //recv(net_fd, fileName, name_len, MSG_WAITALL);

    ////server路径
    ////strcat(path,"/");
    ////strcat(path,fileName);
    ////printf("path = %s\n", path);


    ////3+++++++++
    //
    ////接收客户端文件路径长度
    //int client_path_len = 0;
    //int client_path_len_recv = recv(net_fd, &client_path_len, sizeof(int), MSG_WAITALL);
    //ERROR_CHECK(client_path_len_recv, -1, "recv");
    //printf("client_path_len = %d\n", client_path_len);

    ////客户端文件路径,要上传文件的父路径
    //char client_f_path[1024] = {0};
    //memset(client_f_path, 0, sizeof(client_f_path));
    //int client_path_recv = recv(net_fd, client_f_path, client_path_len, MSG_WAITALL);
    //ERROR_CHECK(client_path_recv, -1, "recv");
    //printf("client_f_path = %s\n", client_f_path);

    //要上传的文件路径
    char client_path[1024] = {0};
    memset(client_path, 0, sizeof(client_path));
    strcpy(client_path, client_f_path);
    strcat(client_path, "/");
    strcat(client_path, fileName);
    printf("client_path = %s\n", client_path);

    //接收客户端文件hash长度
    int md5_len = 0;
    int md5_len_recv = recv(net_fd, &md5_len, sizeof(int), MSG_WAITALL);
    ERROR_CHECK(md5_len_recv, -1, "recv");
    printf("md5_len = %d\n", md5_len);

    //接收客户端文件hash
    char md5[1024] = {0};
    memset(md5, 0, sizeof(md5));
    int md5_recv = recv(net_fd, md5, md5_len, MSG_WAITALL);
    ERROR_CHECK(md5_recv, -1, "recv");
    printf("md5 = %s\n", md5);



    //处理逻辑标记位，将服务器处理逻辑反应给客户端
    //上传1：查表没有对应数据，根据客户端文件的hash值查找，没找到一样的，没人传过
    //秒传2：查表没有对应数据，根据客户端文件的hash值查找，找到一样的，flag为1
    //断点续传4(我没有，他有)：查表没有对应数据，根据客户端文件的hash值查找，找到一样的，flag为0
    //已有文件3：查表有对应数据，根据客户端文件的hash值与数据表中存储的hash比较，一样，再看flag是否是1，又一样
    //断点续传7(我自己有)：查表有对应数据，根据客户端文件的hash值与数据表中存储的hash比较，一样，再看flag为0
    //不允许有同名文件5：查表有对应数据，根据客户端文件的hash值与数据表中存储的hash比较，不一样
    //出错6：各类情况


    //3.2
    int do_flag = 0;
    //查父目录数据获取父目录id
    sql_t my_sql;
    printf("1.1\n");

    sprintf(my_sql.query, "select * from file_table where path = '%s' and user_id = %d", client_f_path, user_id);
    printf("select-query = %s\n", my_sql.query);

    printf("1.2\n");

    int res = mysql_query(conn, my_sql.query);                                                                

    printf("res = %d\n", res);
    MYSQL_ERROR_CHECK(conn,res, 1);

    my_sql.result = mysql_store_result(conn);

    printf("列数：%d\n", mysql_num_fields(my_sql.result));

    my_sql.row = mysql_fetch_row(my_sql.result);
    int father_id = atoi(my_sql.row[0]);
    printf("father_id = %d\n", father_id);
    mysql_free_result(my_sql.result);

    memset(&my_sql, 0, sizeof(my_sql));
    sprintf(my_sql.query, "select * from file_table where path = '%s' and user_id = %d", client_path, user_id);

    res = mysql_query(conn, my_sql.query);                                                                

    printf("res = %d\n", res);
    MYSQL_ERROR_CHECK(conn,res, 1);

    my_sql.result = mysql_store_result(conn);

    printf("列数：%d\n", mysql_num_fields(my_sql.result));

    my_sql.row = mysql_fetch_row(my_sql.result);


    if(true){
        int num1 = mysql_num_rows(my_sql.result);
        printf("行数：%d\n", num1);
        if(num1 == 0){
            //处理不存在记录的情况
            int do_flag = handleNoFileRecord(conn, net_fd, client_path, fileName, md5, father_id, user_id);
            printf("no file, flag = :%d\n", do_flag);
            send_flag(net_fd, do_flag);
            printf("after NoFileRecord\n");
            if(do_flag == 6 || do_flag == 2){
                return 0;
            }
        }else{
            //处理存在记录的情况，应该只有一条
            int file_flag = atoi(my_sql.row[3]);
            char file_md5[1024] = {0};
            memset(file_md5, 0, sizeof(file_md5));
            strncpy(file_md5, my_sql.row[6], sizeof(file_md5) - 1);
            printf("md5_buf = %s\n", file_md5);
            int do_flag = handleFileExistRecord(conn, net_fd, client_path, fileName, md5, file_flag, file_md5, user_id);
            send_flag(net_fd, do_flag);
            if(do_flag == 6 || do_flag == 3 || do_flag == 5){
                return 0;
            }
        }
    }else{
        //查询失败
        do_flag = 6;
        send_flag(net_fd, do_flag);
        return 0;
    }

    //3.2

    mysql_free_result(my_sql.result);
    //到这要传文件了

    //3+++++++++++
    char server_path[1024] = {0};
    strcpy(server_path, path);
    strcat(path, md5);

    printf("path = %s\n", path);
    int file_fd = open(path, O_RDWR|O_CREAT, 0600);

    struct stat st;
    fstat(file_fd, &st);
    off_t real_file_size = st.st_size;

    //给客户端传当前文件大小
    int offset_send = send(net_fd, &real_file_size, sizeof(off_t), MSG_NOSIGNAL);
    ERROR_CHECK(offset_send, -1, "send");

    //计算还有多少没接收的，用于判断
    //这里是完整文件大小与当前文件实际大小的计算


    printf("file_size:%ld\n", file_size);
    printf("real_file_size:%ld\n", real_file_size);

    off_t re_offset = file_size - real_file_size;
    printf("re_offset = %ld\n", re_offset);

    int finish_flag = 0;//成功发1

    //未接收的文件大小<=0
    if(re_offset <= 0){
        printf("秒传\n");
        sql_t new_mysql;
        memset(&new_mysql, 0, sizeof(new_mysql));
        sprintf(new_mysql.query, 
                "select * from file_table where path = '%s' and user_id = %d", 
                client_path, user_id);
        mysql_query(conn, new_mysql.query);
        new_mysql.result  = mysql_store_result(conn);
        new_mysql.row = mysql_fetch_row(new_mysql.result);
        if(new_mysql.row != NULL){
            //更改flag
            memset(&new_mysql, 0, sizeof(new_mysql));
            sprintf(new_mysql.query, 
                    "update file_table set flag = 1 where path = '%s' and user_id = %d", 
                    client_path, user_id);
            mysql_query(conn, new_mysql.query);

            close(file_fd);
            return 0;       
        }else {
            //插入新的
            memset(&(my_sql.query), 0, sizeof(my_sql.query));
            sprintf(my_sql.query, "insert into file_table value(NULL, 0, %d, 0, '%s', '%s', '%s', %d)", 
                    father_id, client_path, fileName, md5, user_id);
            mysql_query(conn, my_sql.query);
            close(file_fd); 
            return 0;
        }
    }

    //计算偏离量,这里截取了当前文件的整数字节大小，为了设置映射偏移量
    //当前文件不满足整4096字节的数据会被ftruncate截断丢弃
    off_t off_reset = real_file_size / 4096 * 4096;
    printf("off_reset = %ld\n", off_reset);

    //调整文件指针移动到文件末尾
    lseek(file_fd, off_reset, SEEK_SET);

    //先设置文件完整大小
    ftruncate(file_fd, file_size);

    printf("--recv begin--\n");

    printf("off_reset = %ld\n", off_reset);
    printf("file_size:%ld\n", file_size);


    int temp_count = 0;
    while(off_reset < file_size){
        //printf("上传次数%d\n", temp_count);

        temp_count++;
        ftruncate(file_fd, off_reset + 1024);
        char buf[1024] = {0};
        memset(buf, 0, sizeof(buf));

        //printf("before\n"); 
        ssize_t len = 0;
        int len_recv = recv(net_fd, &len, sizeof(ssize_t), MSG_WAITALL);
        //if(len == 0){
        //    printf("接收: %ld\n", off_reset);
        //    ftruncate(file_fd, off_reset);
        //    printf("客户端断开\n");
        //    //close(file_fd);
        //    break;

        //}

        //printf("after recv len:%d\n", len_recv);
        //printf("len:%ld\n", len);
        off_t recv_res = recv(net_fd, buf, len, MSG_WAITALL);
        ERROR_CHECK(recv_res, -1, "recv");
        //printf("buf_size:%ld\n", strlen(buf));
        if(recv_res == 0){
            printf("接收: %ld\n", off_reset);
            ftruncate(file_fd, off_reset);
            printf("客户端断开\n");
            close(file_fd);
            //break;
            printf("return \n");
            finish_flag = 1;
            int send_sig = send(net_fd, &finish_flag, sizeof(int), MSG_NOSIGNAL);
            ERROR_CHECK(send_sig, -1, "send_sig");

            return 0;
        }
        //printf("res: %d\n", recv_res);
        //printf("res: %d; buf: %s \n", res, buf);

        write(file_fd, buf, recv_res);
        off_reset = off_reset + recv_res;
        ftruncate(file_fd, off_reset);

        lseek(file_fd, off_reset, SEEK_SET);
        //usleep(2000);
    }

    char sql_word6[1024] = {0};
    memset(sql_word6, 0, sizeof(sql_word6));
    //插入一条数据到file表
    sprintf(sql_word6, "update file_table set flag=1 where path = '%s' and user_id = %d", client_path, user_id);
    printf("sql_word6 = %s\n", sql_word6);
    res = mysql_query(conn, sql_word6);                                                                
    printf("res = %d\n", res);
    if(res != 0){

        //finish_flag = 6;
        int res7_send = send(net_fd, &finish_flag, sizeof(int), MSG_NOSIGNAL);
        ERROR_CHECK(res7_send, -1, "send");
        printf("上传失败7\n");
        close(file_fd);
        return 0;
    }


    //传输完给客户端发1
    finish_flag = 1;
    int send_sig = send(net_fd, &finish_flag, sizeof(int), MSG_NOSIGNAL);
    ERROR_CHECK(send_sig, -1, "send_sig");
    //ftruncate(file_fd, file_size);

    //void *p = mmap(NULL, file_size,  PROT_READ|PROT_WRITE, MAP_SHARED, file_fd, 0);

    ////第一种方法
    //recv(net_fd, p, file_size, MSG_WAITALL);
    //printf("%ld\n", file_size);
    ////第二种方法
    ////recvn(net_fd, p, file_size);

    //munmap(p, file_size);

    printf("--recv end--\n");
    close(file_fd);
    //close(net_fd);

    return 0;

}
